Cayenne User Documentation
Caching Query Results

Cayenne provides a way to cache query results, avoiding unneeded database trips for the frequently used queries. Caching policy is configured per query. Policy can be set via the API or in CayenneModeler.

Upgrading to Cayenne 1.2 and Newer
org.apache.cayenne.query.GenericSelectQuery interface that defined cache policy types is deprecated. Cache policies are now a part of the new org.apache.cayenne.query.QueryMetadata interface.

The following cache policies are supported:

Policy Cache Scope Cache Behavior
(default policy) QueryMetadata.NO_CACHE N/A Always fetch, never use cache, never save to cache
QueryMetadata.LOCAL_CACHE DataContext If result is previously cached, use it, otherwise do a fetch and store result in cache for future use
QueryMetadata.LOCAL_CACHE_REFRESH DataContext Never use cache, alwyas do a fetch and store result in cache for future use
QueryMetadata.SHARED_CACHE DataDomain (usually shared by all contexts in the same JVM) If result is previously cached, use it, otherwise do a fetch and store result in cache for future use
QueryMetadata.SHARED_CACHE_REFRESH DataDomain (usually shared by all contexts in the same JVM) Never use cache, alwyas do a fetch and store result in cache for future use

It is important to understand that caching of result lists is done independently from caching of individual DataObjects and DataRows. Therefore the API is different as well. Also cached results lists are not synchronized across VMs (even the shared cache).

API for Result Caching

Users must set two Query parameters to configure caching - query name that is used as a key to result cache and query cache policy (one of the policies above). Note that if two unrelated queries have the same name, they will hit the same cache entry. This is not a bug, this is a feature that should be taken into consideration when naming queries.

Below we will create a query and set its caching policy to LOCAL_CACHE:

SelectQuery query = new SelectQuery(Artist.class);

// set query name that will be used as a unique key to perform result caching
query.setName("MySelect");

// set local cache policy, meaning the cache will be stored in the DataContext 
// and not shared between different contexts
query.setCachePolicy(GenericSelectQuery.LOCAL_CACHE);

DataContext context = ... // assume this exists

// there is probably no cache at this point, so the query will hit the database
List objects = context.performQuery(query);

Reruning the query in the same DataContext at a later time will be much faster as it will be hitting the cache:

List objects1 = context.performQuery(query);

Here we want to refresh the cache, but still keep caching the fresh result:

query.setCachePolicy(GenericSelectQuery.LOCAL_CACHE_REFRESH);

List objects2 = context.performQuery(query);

The example above shows caching with SelectQuery, but it works exactly the same way for SQLTemplate and ProcedureQuery. Similarly SHARED_CACHE and SHARED_CACHE_REFRESH cache policies create cache shared by all DataDontexts that work with a given DataDomain.

Upgrading to Cayenne 1.2 and Newer

Cache refreshing API has changed in 1.2. Cayenne 1.1 relied on the use of SelectQuery.setRefreshingObjects(..) to determine whether to expire cached result lists. This is no longer the case (setting this flag only refreshes individual objects as it should, and has no effect whatsoever on list caching). Instead caching and cache refreshing is controlled by the cache policy as described above.

Queries Mapped in CayenneModeler

The easiest way to set up caching is by creating a named query in CayenneModeler with the appropriate caching type.

Then it can be executed via DataContext:

List objects1 = context.performQuery("MyQuery", false);

The second "false" parameter above indicated that if possible, cached result should be used. Now if we want to force refresh, it can be changed to true (for just this invocation - this does not affect the underlying saved query)

List objects2 = context.performQuery("MyQuery", true);

Note that parameterized named queries will still work correctly with the cache. We've already mentioned that the users must ensure that two queries must have different names if they fetch logically different data. This is NOT the case with queries stored in the DataMap. If you run the same named query with different sets of parameters, Cayenne will internally generate unique cache keys for each distinct parameter set.

Map parameters = Collections.singletonMap("key", "value1");
List objects1 = context.performQuery("MyQuery", parameters, false);

Now if we run the same query with a different set of parameters, Cayenne will do the right thing and create a separate entry in the cache:

Map parameters = Collections.singletonMap("key", "value2");
List objects2 = context.performQuery("MyQuery", parameters, false);
.